﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using log4net;
using LanguageDetector.Data;

namespace LanguageDetector
{
    public class DetectorLogic
    {
        #region factory singleton
        
        private class InnerSingleton
        {
            internal readonly static DetectorLogic self = new DetectorLogic();
            static InnerSingleton() { }
        }
        public static DetectorLogic GetInstance() { return InnerSingleton.self; }

        #endregion

        ILog _logger = LogManager.GetLogger(typeof(DetectorLogic));
        readonly TimeSpan UPDATE_FREQUENCY = TimeSpan.Zero;
        readonly double MINIMUM_COUNT_PCT = 0.1; 
        private DetectorLogic()
        {
            var update = ConfigurationManager.AppSettings["DetectorLogic_UpdateFrequency"];
            TimeSpan up;
            if (!string.IsNullOrEmpty(update) && TimeSpan.TryParse(update, out up))
                UPDATE_FREQUENCY = up;

            var mincount = ConfigurationManager.AppSettings["DetectorLogic_MinCountPCT"];
            double pct;
            if (!string.IsNullOrEmpty(mincount) && double.TryParse(mincount, out pct))
                MINIMUM_COUNT_PCT = pct;

            this.FetchLanguageStats();
        }

        #region data needed for detection

        DateTime _lastFetched = DateTime.MinValue;
        Dictionary<LanguageStat, BiGramStat> _langMap = null;
        Dictionary<string, List<LanguageStat>> _encMap = null;
        void FetchLanguageStats()
        {
            lock (this)
            {
                try
                {
                    if (_langMap.IsNullOrEmpty() || _encMap.IsNullOrEmpty() || 
                        (UPDATE_FREQUENCY > TimeSpan.Zero && DateTime.Now.Subtract(_lastFetched) > UPDATE_FREQUENCY))
                    {
                        _lastFetched = DateTime.Now;

                        var lstats = LanguageStatDB.Get(new LanguageCriteria());
                        if (!lstats.IsNullOrEmpty())
                        {
                            var bstats = BiGramStatDB.Get(new BiGramCriteria());
                            if (bstats.IsNullOrEmpty())
                                _logger.WarnFormat("Fetched BigramStats is null or empty!");

                            var encs = CharacterEncodingDB.GetAll();
                            if (encs.IsNullOrEmpty())
                                _logger.WarnFormat("Fetched Encodings is null or empty!");

                            if (!bstats.IsNullOrEmpty() && !encs.IsNullOrEmpty())
                            {
                                _langMap = (from b in bstats
                                            join l in lstats on b.LanguageID equals l.ID
                                            let t = new { Lang = l, Bigram = b }
                                            select t).ToDictionary(p => p.Lang, p => p.Bigram);
                                _encMap = (from e in encs
                                           let t = new { EncName = e.Encoding, Langs = from l in lstats where e.LanguageIDs.Contains(l.ID) select l }
                                           select t).ToDictionary(p => p.EncName, p => p.Langs.ToList());
                            }
                        }
                        else
                            _logger.WarnFormat("Fetched LanguageStats is null or empty.");
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error("Error fetching stats data.", ex);
                }
            }
        }

        #endregion

        public DetectionResult Detect(string content)
        {
            var res = new DetectionResult();
            try
            {
                if (content == null || content.Length <= 3)
                    res.Error = new ArgumentNullException("Detection content is null or empty.");
                else
                {
                }
            }
            catch (Exception ex) 
            {
                res.Error = ex;
                _logger.ErrorFormat("Detection error. Input: \"{0}\". Error: {1}", content, ex);
            }
            return res;
        }
    }
}
